package com.agileai.hotweb.controller.core;

import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;

import com.agileai.domain.DataMap;
import com.agileai.domain.DataParam;
import com.agileai.hotweb.annotation.PageAction;
import com.agileai.hotweb.bizmoduler.core.OperationAuthInterceptor;
import com.agileai.hotweb.common.BeanFactory;
import com.agileai.hotweb.common.HandlerParser;
import com.agileai.hotweb.domain.ParamTrace;
import com.agileai.hotweb.domain.core.Profile;
import com.agileai.hotweb.renders.LocalRenderer;
import com.agileai.hotweb.renders.ViewRenderer;
import com.agileai.util.MapUtil;
import com.agileai.util.StringUtil;

abstract public class BaseHandler {
	public static class OperaType{
		public static final String KEY = "operaType";

		public static final String CREATE = "insert";
		public static final String UPDATE = "update";
		public static final String COPY = "copy";
		public static final String DELETE = "delete";
		public static final String DETAIL =  "detail";
	}
	
	protected transient Map<String, Method> pageActionMethodsMap = Collections.synchronizedMap(new HashMap<String,Method>());
	
	public static final String  ERROR_MSG_KEY = "errorMsg";
	public static final String PARAM_TRACE = "PARAM_TRACE";
	public static final String ACTION_TYPE = "actionType";
	public static final String DEFAULT_ACTION_TYPE = "prepareDisplay";
	
	public static final String SUCCESS = "success";
	public static final String FAIL = "fail";
	public static final String Yes = "Y";
	public static final String No = "N";
	
	public static final String HTTP_SESSION_MODEL_KEY = "sessionAttributesKey";

	protected String defDuplicateMsg = "指定主键或者编码的记录已存在，请检查！";
	
	protected Logger log = Logger.getLogger(this.getClass());
	
	private DataMap attributesContainer = new DataMap();
	protected String rsIdTag = "rsId";
	
	protected String handlerId = null;
	protected String serviceId = null;
	
	protected HttpServletRequest request = null;
	protected HttpServletResponse response = null;
	
	protected HttpServlet dispatchServlet = null;
	
	@SuppressWarnings("rawtypes")
	private List rsList = new ArrayList();
	private String operaType = null;
	protected String page = null;
	
	public BaseHandler(){
		this.cacheMethod();
	}

	public static boolean isAjaxRequest(HttpServletRequest request){
		// jquery等会默认添加此配置项,自己手写ajax请求则需要手工添加 自定义请求头
		// xmlhttp.setRequestHeader("X-Requested-With","XMLHttpRequest");
        String requestType = request.getHeader("X-Requested-With");
        boolean isAjaxRequest = "XMLHttpRequest".equals(requestType);
        return isAjaxRequest;
    }
	
	protected boolean isReqRecordOperaType(String operaType){
		return OperaType.UPDATE.equals(operaType) 
				|| OperaType.COPY.equals(operaType)
				|| OperaType.DETAIL.equals(operaType);
	}
	
	@SuppressWarnings("rawtypes")
	private void cacheMethod() {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		String clazzName = this.getClass().getName();
		try {
			Class clazz = classLoader.loadClass(clazzName);
			Method[] methords = clazz.getMethods();
			for (Method localMethod : methords) {
				if (localMethod.getAnnotation(PageAction.class) == null)
					continue;
				String id = localMethod.getName();
				this.pageActionMethodsMap.put(id, localMethod);
			}
		} catch (Exception e) {
			Logger.getRootLogger().error(e.getLocalizedMessage(),e);
		}
	}
	
	@SuppressWarnings("unchecked")
	protected void mergeParam(DataParam param){
		List<ParamTrace> paramTraceList = (List<ParamTrace>)this.getSessionAttribute(PARAM_TRACE);
		if (paramTraceList != null && paramTraceList.size() > 0){
			ParamTrace paraTrace = paramTraceList.get(paramTraceList.size()-1);
			
			if (paraTrace != null && paraTrace.getHandlerId().equals(this.handlerId)){
				DataMap queryParam = paraTrace.getPageParam();
				param.append(queryParam);
				paramTraceList.remove(paramTraceList.size()-1);
			}
		}
	}
	protected boolean isTrue(String expression){
		return "1".equals(expression)  
			|| "y".equals(expression.toLowerCase())
			|| "yes".equals(expression.toLowerCase())
			|| "true".equals(expression.toLowerCase());
	}
	
	@SuppressWarnings("unchecked")
	protected void storeParam(DataParam param){
		List<ParamTrace> paramTraceList = (List<ParamTrace>)this.getSessionAttribute(PARAM_TRACE);
		if (paramTraceList == null){
			paramTraceList = new ArrayList<ParamTrace>();
			this.putSessionAttribute(PARAM_TRACE, paramTraceList);
		}
		ParamTrace paramTrace = new ParamTrace(this.handlerId,param);
		paramTraceList.add(paramTrace);
	}
	@SuppressWarnings("unchecked")
	protected void clearParam(String handlerId){
		List<ParamTrace> paramTraceList = (List<ParamTrace>)this.getSessionAttribute(PARAM_TRACE);
		if (paramTraceList == null){
			paramTraceList = new ArrayList<ParamTrace>();
			this.putSessionAttribute(PARAM_TRACE, paramTraceList);
		}
		List<ParamTrace> newParamTraceList =  new ArrayList<ParamTrace>();
		for (int i=0;i < paramTraceList.size();i++){
			ParamTrace paramTrace = paramTraceList.get(i);
			String tempHandlerId = paramTrace.getHandlerId(); 
			if (tempHandlerId != null && tempHandlerId.equals(handlerId)){
				continue;
			}
			newParamTraceList.add(paramTrace);
		}
		this.putSessionAttribute(PARAM_TRACE, newParamTraceList);
	}
	protected void clearParam(){
		this.removeSessionAttribute(PARAM_TRACE);
	}
	@SuppressWarnings("unchecked")
	protected String getLastHanderId(){
		String result = null;
		List<ParamTrace> paramTraceList = (List<ParamTrace>)this.getSessionAttribute(PARAM_TRACE);
		if (paramTraceList != null && !paramTraceList.isEmpty()){
			ParamTrace lasTrace = paramTraceList.get(paramTraceList.size()-1);
			result = lasTrace.getHandlerId();
		}
		return result;
	}
	protected void processPageAttributes(DataParam param){
	}
	protected void initParameters(DataParam param){
	}
	protected void initParamItem(DataParam param,String htmlName,String value){
		if (!param.containsKey(htmlName)){
			param.put(htmlName,value);
		}
	}
	public DataMap getAttributesContainer() {
		return attributesContainer;
	}
	public void setAttributesContainer(DataMap attributesContainer) {
		this.attributesContainer = attributesContainer;
	}
	public String getOperaType() {
		return operaType;
	}
	public void setOperaType(String operateType) {
		this.operaType = operateType;
	}
	@SuppressWarnings("rawtypes")
	public List getRsList() {
		return rsList;
	}
	@SuppressWarnings("rawtypes")
	public void setRsList(List rsList) {
		if (rsList != null)
		this.rsList = rsList;
	}
	public void setAttributes(DataMap param) {
		if (!MapUtil.isNullOrEmpty(param)){
			Iterator<String> iter = param.keySet().iterator();
			while(iter.hasNext()){
				String key = (String)iter.next();
				Object value = param.get(key);
				this.setAttribute(key, value);
			}
		}
	}
	public void setAttribute(String key,Object value){
		this.attributesContainer.put(key,value);
	}
	public void setAttribute(DataParam param,String key){
		String value = param.get(key);
		this.attributesContainer.put(key,value);
	}
	public void setAttribute(DataParam param,String[] keys){
		for (int i=0;i < keys.length;i++){
			String key = keys[i];
			String value = param.get(key);
			this.attributesContainer.put(key,value);	
		}
	}
	public Object getAttribute(String key){
		return attributesContainer.get(key);
	}
	public Object getAttribute(String key,Object defaultValue){
		Object result = attributesContainer.get(key); 
		if (result == null){
			result = defaultValue;
		}
		return result;
	}
	public boolean hasAttribute(String key){
		return attributesContainer.containsKey(key);
	}
	public String getAttributeValue(String key){
		return String.valueOf(attributesContainer.get(key));
	}
	public String getAttributeValue(String key,String defValue){
		String result = getAttributeValue(key);
		if (StringUtil.isNullOrEmpty(result)){
			result = defValue;
		}
		return result;
	}
	public String getOperaAttributeValue(String key,String defValue){
		String result = getAttributeValue(key);
		if (OperaType.CREATE.equals(this.getOperaType()) && StringUtil.isNullOrEmpty(result) ){
			result = getAttributeValue(key,defValue);
		}
		return result;
	}
	public String getDetailOrUpdateAttributeValue(String key,String defValue){
		String result = "";
		if (OperaType.DETAIL.equals(this.getOperaType()) && OperaType.UPDATE.equals(this.getOperaType())){
			result = getAttributeValue(key,defValue);
		}
		return result;
	}
	public String getCreateAttributeValue(String key,String defValue){
		String result = "";
		if (OperaType.CREATE.equals(this.getOperaType())){
			result = getAttributeValue(key,defValue);
		}
		return result;
	}
	protected void clearSession(){
		this.getSessionAttributes().clear();
		this.getRequest().getSession().invalidate();
	}
	protected void removeSessionAttribute(String sessionKey){
		this.getSessionAttributes().remove(sessionKey);
	}
	protected void putSessionAttribute(String key,Object object){
		this.getSessionAttributes().put(key, object);
	}
	
	@SuppressWarnings("unchecked")
	public HashMap<String,Object> getSessionAttributes(){
		HttpSession session = request.getSession();
		Object temp = session.getAttribute(HTTP_SESSION_MODEL_KEY);
		if (temp == null){
			HashMap<String,Object> sessionAttributes = new HashMap<String,Object>();
			session.setAttribute(HTTP_SESSION_MODEL_KEY,sessionAttributes);
			temp = sessionAttributes;
		}
		return (HashMap<String,Object>)temp;
	}
	protected Object getSessionAttribute(String key){
		return getSessionAttributes().get(key);
	}
	protected Object getAndRemoveSessionAttribute(String key){
		Object result = getSessionAttributes().get(key);
		if (getSessionAttributes().containsKey(key)){
			getSessionAttributes().remove(key);
		}
		return result;
	}
	protected Profile getProfile(){
		HttpSession session = request.getSession();
		Profile profile = (Profile)session.getAttribute(Profile.PROFILE_KEY);
		return profile;
	}
	protected Principal getPrincipal(){
		HttpSession session = request.getSession();
		Principal principal = (Principal)session.getAttribute(Profile.PROFILE_KEY);
		return principal;
	}
	public Object getUser(){
		Object user = null;
		Profile profile = getProfile();
		if (profile != null){
			user = profile.getUser();			
		}
		return user;
	}
	protected void initMappingItem(String htmlName,DataMap items){
		request.setAttribute(htmlName,items);
	}
	public void setRsIdTag(String rsIdTag) {
		this.rsIdTag = rsIdTag;
	}
	public void setDispatchServlet(HttpServlet dispatchServlet) {
		this.dispatchServlet = dispatchServlet;
	}
	public HttpServletRequest getRequest() {
		return request;
	}
	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}
	public HttpServlet getDispatchServlet() {
		return dispatchServlet;
	}
	private class OperationAuthFailedViewRenderer extends ViewRenderer {
		private String handlerCode = null;
		private String actionType = null;
		
		public OperationAuthFailedViewRenderer(String handlerCode,String actionType){
			this.handlerCode = null;
			this.actionType = actionType;
		}
		
		@Override
		public void executeRender(HttpServlet httpServlet,
				HttpServletRequest request, HttpServletResponse response)
				throws Exception {
			if (isAjaxRequest(request)){
				String codeType = "text/xml";
				response.setHeader("Cache-Control","no-cache");
				response.setHeader("Pragma","no-cache");
				response.setDateHeader("Expires",0); 
				response.setContentType(codeType+";charset=UTF-8");
				PrintWriter out = response.getWriter();
				out.write("You does't have " + handlerCode + " handler's " + actionType + " operation is not validation !");
				out.close();
			} else {
				String fileUrl = LocalRenderer.PATH_FIX + "frame/Error.jsp";
				RequestDispatcher requestDispatcher = httpServlet.getServletContext().getRequestDispatcher(fileUrl);
				requestDispatcher.forward(request, response);
			}
		}
	}
	public ViewRenderer processRequest(DataParam param){
		ViewRenderer result = null;
		try {
			String actionType = getActionType();
			Method method = null;
			String methodName = null;
			boolean authResult = true;
			ServletContext servletContext = this.dispatchServlet.getServletContext(); 
			String operaAuthInterceptorClazz = servletContext.getInitParameter(OperationAuthInterceptor.ParamKey);
			OperationAuthInterceptor operationAuthInterceptor = parseOperationAuthInterceptor(operaAuthInterceptorClazz);
			if (operationAuthInterceptor != null && getUser() != null 
					&& !DEFAULT_ACTION_TYPE.equals(actionType)){
				authResult = operationAuthInterceptor.authenticate(getUser(),this, actionType);
			}
			if (authResult){
				if (!DEFAULT_ACTION_TYPE.equals(actionType) && this.pageActionMethodsMap.containsKey(actionType)){
					method = this.pageActionMethodsMap.get(actionType);
					methodName = method.getName();
				}else{
					methodName = getMethodName(actionType);
					method = getClass().getMethod(methodName,new Class[] {DataParam.class});
				}				
			}else{
				result = new OperationAuthFailedViewRenderer(getHandlerId(),actionType);
			}
			log.info(methodName + "------start");
			Object[] arg = new Object[] {param};
			result = (ViewRenderer)method.invoke(this, arg);
			log.info(methodName + "------end");
		} catch (Exception e) {
			log.error(e.getLocalizedMessage(),e);
		}
		return result;
	}
	private OperationAuthInterceptor parseOperationAuthInterceptor(String clazzName){
		OperationAuthInterceptor result = null;
		try {
			if (!StringUtil.isNullOrEmpty(clazzName)){
				result = (OperationAuthInterceptor)Class.forName(clazzName).newInstance();				
			}
		} catch (Exception e) {
			log.error(e.getLocalizedMessage(),e);
		}
		return result;
	}
	protected String getActionType(){
		String result = DEFAULT_ACTION_TYPE;
		boolean isEntry = StringUtil.isNullOrEmpty(request.getParameter(ACTION_TYPE));
		result = isEntry ? DEFAULT_ACTION_TYPE:request.getParameter(ACTION_TYPE);
		return result;
	}
	private String getMethodName(String actionType){
		StringBuffer sb = new StringBuffer();
		if (!DEFAULT_ACTION_TYPE.equals(actionType)){
			sb.append("do").append(StringUtil.upperFirst(actionType)).append("Action");			
		}else{
			sb.append(DEFAULT_ACTION_TYPE);
		}
		return sb.toString();
	}
	protected Object lookupService(String serviceId){
		Object service = BeanFactory.instance().getBean(serviceId);
		return service;
	}
	protected <ServiceType> ServiceType lookupService(Class<ServiceType> serviceClass){
		String serviceId = buildServiceId(serviceClass);
		Object service = BeanFactory.instance().getBean(serviceId);
		return serviceClass.cast(service);
	}
	public String getServiceId() {
		return serviceId;
	}
	public void setServiceId(String serviceId) {
		this.serviceId = serviceId;
	}
	public String getHandlerId() {
		return handlerId;
	}
	public void setHandlerId(String handlerId) {
		this.handlerId = handlerId;
	}
	public String getHandlerURL(){
		String servletURL = dispatchServlet.getInitParameter("servletURL");
		StringBuffer handlerURL = new StringBuffer();
		handlerURL.append(servletURL).append("?").append(getHandlerId());
		return handlerURL.toString();
	}
	
	@SuppressWarnings("rawtypes")
	protected String getHandlerURL(Class handlerClazz){
		String result = null;
		String className = handlerClazz.getName();
		String handlerId = HandlerParser.getOnly().parseHandlerId(className);
		result = getHandlerURL(handlerId);
		return result;
	}
	protected String getHandlerURL(String handlerId){
		String result = null;
		String servletURL = dispatchServlet.getInitParameter("servletURL");
		StringBuffer handlerURL = new StringBuffer();
		handlerURL.append(servletURL).append("?").append(handlerId);
		result = handlerURL.toString();		
		return result;
	}
	public ViewRenderer prepareDisplay(DataParam param){
		return new LocalRenderer(getPage());
	}
	public void setResponse(HttpServletResponse response) {
		this.response = response;
	}
	protected void setErrorMsg(String errorMsg){
		this.getRequest().setAttribute(ERROR_MSG_KEY, errorMsg);
	}
	public String getPage() {
		return page;
	}
	public void setPage(String page) {
		this.page = page;
	}
	@SuppressWarnings("rawtypes")
	public static String buildServiceId(Class serviceClass){
		String result = null;
		String className = serviceClass.getSimpleName();
		if (className.endsWith("Service")){
			result = StringUtil.lowerFirst(className);			
		}else{
			result = StringUtil.lowerFirst(className) + "Service";
		}
		return result;
	}
}
